package org.zebra.mybatis.plus.curd;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.TypeUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.ApiOperation;
import org.dromara.streamquery.stream.plugin.mybatisplus.Database;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.zebra.common.core.Result;
import org.zebra.common.core.ValidGroup;
import org.zebra.common.exception.StatusCode;
import org.zebra.common.util.ReflectPlusUtil;
import org.zebra.mybatis.plus.core.MPageQuery;
import org.zebra.mybatis.plus.core.ServicePlusImpl;
import org.zebra.mybatis.plus.core.query.QueryHelper;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @param <S> 服务实现
 * @param <Q> 以Query结尾的vo对象
 * @param <C> 以Command结尾的vo对象
 * @param <P> MPageQuery 类或子类 vo对象
 * @param <R> 以Result结尾的vo对象
 * @author zhanghongbin
 */
public abstract class CrudController<S extends ServicePlusImpl, Q, C, P extends MPageQuery, R> {

    @Autowired
    protected S baseService;

    private Class<R> resultClass;

    private ConcurrentHashMap<String, Method> cacheMethod;

    protected CrudController() {
        resultClass = (Class) TypeUtil.getTypeArgument(getClass(), 4);
        cacheMethod = new ConcurrentHashMap<>();
    }

    @ApiOperation(value = "增加操作", notes = "框架自动生成增加操作")
    @PostMapping
    public final Result<Boolean> add(@RequestBody @Validated(ValidGroup.Create.class) C command) {
        Method method = this.getMethod("add", command.getClass());
        this.before(method, command);
        Result<Boolean> result;
        if (command.getClass() == baseService.getEntityClass()) {
            result = ok(baseService.getServicePlus().save(command));
        } else {
            result = ok(baseService.getServicePlus().save(BeanUtil.toBean(command, baseService.getEntityClass())));
        }
        this.after(method, command, result);
        return result;
    }

    @ApiOperation(value = "修改操作", notes = "框架自动生成修改操作", response = Result.class)
    @PutMapping
    public final Result<Boolean> update(@RequestBody @Validated(ValidGroup.Update.class) C command) {
        Method method = this.getMethod("update", command.getClass());
        this.before(method, command);
        Result<Boolean> result;
        if (command.getClass() == baseService.getEntityClass()) {
            result = ok(baseService.getServicePlus().updateById(command));
        } else {
            result =
                    ok(baseService.getServicePlus().updateById(BeanUtil.toBean(command, baseService.getEntityClass())));
        }
        this.after(method, command, result);
        return result;
    }

    @ApiOperation(value = "根据id查询数据操作", notes = "框架自动生成根据id查询数据操作")
    @GetMapping("/{id}")
    public final Result<R> getById(@PathVariable("id") Object id) {
        Method method = this.getMethod("getById", id.getClass());
        this.before(method, id);
        Result<R> result;
        if (resultClass == baseService.getEntityClass()) {
            result = ok((R) Database.execute(
                    baseService.getEntityClass(), baseMapper -> baseMapper.selectById((Serializable) id)));
        } else {
            Object o = Database.execute(
                    baseService.getEntityClass(), baseMapper -> baseMapper.selectById((Serializable) id));
            result = ok(BeanUtil.toBean(o, resultClass));
        }
        this.after(method, id, result);
        return result;
    }

    @ApiOperation(value = "根据条件查列表询数据操作", notes = "框架自动生成根据条件查列表询数据操作")
    @GetMapping("/list")
    public final Result<List<R>> findList(Q query) {
        Method method = this.getMethod("findList", query.getClass());
        this.before(method, query);
        Result<List<R>> result;
        QueryWrapper queryWrapper = QueryHelper.build(query);
        queryWrapper.setEntityClass(baseService.getEntityClass());
        if (resultClass == baseService.getEntityClass()) {
            result = ok(Database.list(queryWrapper));
        } else {
            result = ok(BeanUtil.copyToList(Database.list(queryWrapper), resultClass));
        }
        this.after(method, query, result);
        return result;
    }

    @ApiOperation(value = "根据id删除操作", notes = "框架自动生成根据id删除操作")
    @DeleteMapping("/{id}")
    public final Result<Boolean> removeById(@PathVariable("id") Object id) {
        Method method = this.getMethod("removeById", id.getClass());
        this.before(method, id);
        Result<Boolean> result;
        Integer count = (Integer)
                Database.execute(baseService.getEntityClass(), baseMapper -> baseMapper.deleteById((Serializable) id));
        if (count >= 1) {
            result = ok(true);
        } else {
            result = ok(false);
        }
        this.after(method, id, result);
        return result;
    }

    @ApiOperation(value = "根据id列表删除操作", notes = "框架自动生成根据id列表删除操作,多个用,分割")
    @DeleteMapping
    public final Result<Boolean> removeByIds(@RequestParam("ids") List<Object> ids) {
        Method method = this.getMethod("removeByIds", ids.getClass());
        this.before(method, ids);
        Result<Boolean> result;
        Integer count =
                (Integer) Database.execute(baseService.getEntityClass(), baseMapper -> baseMapper.deleteBatchIds(ids));
        if (count >= 1) {
            result = ok(true);
        } else {
            result = ok(false);
        }
        this.after(method, ids, result);
        return result;
    }

    @ApiOperation(value = "根据条件分页查询操作", notes = "框架自动生成根据条件分页查询操作")
    @GetMapping("/page")
    public final Result<IPage<R>> findByPage(P pageQuery) {
        Method method = this.getMethod("findByPage", pageQuery.getClass());
        this.before(method, pageQuery);
        Result<IPage<R>> result;
        QueryWrapper queryWrapper = QueryHelper.build(pageQuery);
        queryWrapper.setEntityClass(baseService.getEntityClass());
        IPage iPage = Database.page(pageQuery.build(), queryWrapper);
        if (resultClass != baseService.getEntityClass()) {
            iPage.setRecords(BeanUtil.copyToList(iPage.getRecords(), resultClass));
        }
        result = ok(iPage);
        this.after(method, pageQuery, result);
        return result;
    }

    private <T> Result<T> ok(T data) {
        return new Result<T>()
                .toBuilder()
                        .code(StatusCode.SUCCESS.getCode())
                        .msg(StatusCode.SUCCESS.getMsg())
                        .data(data)
                        .build();
    }

    private Method getMethod(String methodName, Class methodParameterType) {
        Method method = cacheMethod.get(methodName);
        if (method == null) {
            method = ReflectPlusUtil.getMethod(this.getClass(), methodName, methodParameterType);
            cacheMethod.put(methodName, method);
        }
        return method;
    }

    protected void before(Method method, Object params) {}

    protected void after(Method method, Object params, Object result) {}
}
